package com.qingcloud.fexecutor.base.executor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.Page;
import cn.hutool.db.PageResult;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.MongoCommandException;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoClient;
import com.mongodb.client.model.Filters;
import com.qingcloud.fexecutor.base.constant.DateFormatterConstant;
import com.qingcloud.fexecutor.base.enums.CollectionOperateTypeEnum;
import com.qingcloud.fexecutor.base.enums.SqlQueryTypeEnum;
import com.qingcloud.fexecutor.base.pojo.WebPageDataParam;
import com.qingcloud.fexecutor.base.utils.ApiParameterUtil;
import com.qingcloud.fexecutor.base.utils.CustomMapperUtils;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Protocol;

import javax.sql.DataSource;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Pattern;

@Service
@Slf4j
public class DataSourceExecutor {

    /**
     * redis指令执行
     */
    public static void createMongoIdIndex(MongoClient mongoClient, Integer appId, String collection) {
        try {
            String database = getAppDataBase(appId);
            Document document = new Document();
            document.put("id", 1);
            mongoClient.getDatabase(database).createCollection(collection);
            mongoClient.getDatabase(database).getCollection(collection)
                    .createIndex(document);
        } catch (MongoCommandException e) {
            if (!e.getMessage().contains("NamespaceExists")) {
                throw e;
            }
        }

    }

    /**
     * 获取APP数据库
     */
    private static String getAppDataBase(Integer appId) {
        return "app" + appId;
    }

    /**
     * redis指令执行
     */
    public static Object redisExecute(WebPageDataParam.PageParam pageParam, Jedis jedis) {
        String redisCmd = pageParam.getSql();
        if (redisCmd.contains("#{") || redisCmd.contains("${")) {
            //redis命令中有参数变量
            Collection<String> params = CustomMapperUtils.getParameterName(redisCmd);
            cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(pageParam.getJson());
            for (String param : params) {
                Object object = jsonObject.getByPath(param);
                redisCmd = redisCmd.replace("#{" + param + "}", String.valueOf(object))
                        .replace("${" + param + "}", String.valueOf(object));
            }
        }
        Object result = null;
        for (String subCmd : redisCmd.split(";")) {
            if (StrUtil.isNotBlank(subCmd)) {
                subCmd = subCmd.trim();
                String[] split = subCmd.split("\\s+");
                Protocol.Command command = Protocol.Command.valueOf(split[0].toUpperCase());
                String[] args = new String[split.length - 1];
                for (int i = 1; i < split.length; i++) {
                    args[i - 1] = split[i];
                }
                result = jedis.sendCommand(command, args);
            }
        }
        if (Objects.nonNull(result)) {
            if (result instanceof byte[]) {
                result = new String((byte[]) result, StandardCharsets.UTF_8);
            }
            if (result instanceof List<?> tempList) {
                List<String> resultList = new ArrayList<>();
                for (Object tempObject : tempList) {
                    if (tempObject instanceof byte[]) {
                        resultList.add(new String((byte[]) tempObject, StandardCharsets.UTF_8));
                    }
                }
                return resultList;
            }
            if (result instanceof Number) {
                return result;
            }
        }
        return result;
    }

    /**
     * sql执行数据
     */
    public static Object sqlExecute(Integer page, Integer pageSize, String json, String sql, DataSource dataSource) throws Exception {
        //格式化sql
        if (StrUtil.isNotBlank(json) && json.trim().startsWith("{")) {
            JSONObject jsonObject = JSONObject.parseObject(json);
            Map<String, Object> params = jsonObject.getInnerMap();
            sql = CustomMapperUtils.parseMybatisSql(sql, params);
        }
        log.debug("execute sql is {}", sql);
        if (Objects.nonNull(page) && Objects.nonNull(pageSize) && page > 0) {
            //分页查询数据
            Page pageInfo = new Page();
            pageInfo.setPageNumber(page - 1);
            pageInfo.setPageSize(pageSize);
            PageResult<Entity> pageResult = Db.use(dataSource).page(sql, pageInfo);
            com.qingcloud.base.pojo.PageResult pageDTO = new com.qingcloud.base.pojo.PageResult();
            pageDTO.setCount((long) pageResult.getTotal());
            pageDTO.setData(pageResult);
            return pageDTO;
        } else if (sql.trim().toLowerCase().startsWith("select")) {
            //其他查询，默认限制1000条数据
            Page pageInfo = new Page();
            pageInfo.setPageNumber(0);
            pageInfo.setPageSize(1000);
            PageResult<Entity> pageResult = Db.use(dataSource).page(sql, pageInfo);
            return new ArrayList<>(pageResult);
        } else {
            int rows = 0;
            for (String subSql : sql.split(";")) {
                rows += Db.use(dataSource).execute(subSql);
            }
            return rows;
        }
    }

    /**
     * 转换参数
     */
    private static Bson getDocument(String items, Integer operateType, JSONObject paramJson) {
        Document document = new Document();
        if (StrUtil.isBlank(items)) {
            return document;
        }
        if (CollectionOperateTypeEnum.QUERY.code.equals(operateType)) {
            JSONArray jsonArray = JSONArray.parseArray(items);
            List<Bson> filterList = new ArrayList<>(jsonArray.size());
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject conditionObject = jsonArray.getJSONObject(i);
                Integer condition = conditionObject.getInteger("condition");
                String name = conditionObject.getString("name");
                Object value = conditionObject.get("value");
                if (value != null && StrUtil.isNotBlank(value.toString())) {
                    if (SqlQueryTypeEnum.GT.code.equals(condition)) {
                        filterList.add(Filters.gt(name, value));
                    }
                    if (SqlQueryTypeEnum.GE.code.equals(condition)) {
                        filterList.add(Filters.gte(name, value));
                    }
                    if (SqlQueryTypeEnum.EQ.code.equals(condition)) {
                        filterList.add(Filters.eq(name, value));
                    }
                    if (SqlQueryTypeEnum.LT.code.equals(condition)) {
                        filterList.add(Filters.lt(name, value));
                    }
                    if (SqlQueryTypeEnum.LE.code.equals(condition)) {
                        filterList.add(Filters.lte(name, value));
                    }
                    if (SqlQueryTypeEnum.NEQ.code.equals(condition)) {
                        filterList.add(Filters.ne(name, value));
                    }
                    if (SqlQueryTypeEnum.IN.code.equals(condition)) {
                        filterList.add(Filters.in(name, value));
                    }
                    if (SqlQueryTypeEnum.LIKE.code.equals(condition)) {
                        filterList.add(Filters.regex(name, Pattern.compile(".*" + value + ".*")));
                    }
                }
            }
            if (CollUtil.isEmpty(filterList)) {
                return new Document();
            }
            return Filters.and(filterList);
        }
        if (CollectionOperateTypeEnum.ADD.code.equals(operateType)) {
            //添加数据
            JSONArray jsonArray = JSONArray.parseArray(items);
            document.put("id", IdUtil.getSnowflakeNextIdStr());
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                document.put(jsonObject.getString("name"), jsonObject.get("value"));
            }
            document.put("添加时间", LocalDateTime.now().format(DateFormatterConstant.NORMAL_FULL_FORMATTER));
            document.put("添加人", paramJson.getJSONObject("currentUser").getString("name"));
            return document;
        }
        if (CollectionOperateTypeEnum.MODIFY.code.equals(operateType)) {
            //添加数据
            JSONArray jsonArray = JSONArray.parseArray(items);
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                document.put(jsonObject.getString("name"), jsonObject.get("value"));
            }
            document.put("修改时间", LocalDateTime.now().format(DateFormatterConstant.NORMAL_FULL_FORMATTER));
            document.put("修改人", paramJson.getJSONObject("currentUser").getString("name"));
            return new Document("$set", document);
        }
        return document;
    }

    /**
     * 文档转map
     */
    private static Map<String, Object> document2Map(Document item) {
        Map<String, Object> map = new HashMap<>();
        for (String key : item.keySet()) {
            if (!"_id".equals(key)) {
                map.put(key, item.get(key));
            }
        }
        return map;
    }

    /**
     * mongo执行数据
     */
    public static Object mongoExecute(Integer page, Integer pageSize, String json, String actions, Integer appId, MongoClient mongoClient) {
        //提取actions信息
        JSONObject jsonObject = JSONObject.parseObject(actions);
        Integer operateType = jsonObject.getInteger("operateType");
        String collection = jsonObject.getString("resCollection");
        String database = getAppDataBase(appId);

        String items = jsonObject.getString("operateItems");
        //变量替换
        items = ApiParameterUtil.replaceValue(items, json);
        //参数对象
        JSONObject paramJson = JSONObject.parseObject(json);
        //获取参数对象
        Bson document = getDocument(items, operateType, paramJson);
        log.debug("mongo bson is {}", document);

        if (Objects.nonNull(page) && Objects.nonNull(pageSize) && page > 0) {
            //分页查询数据
            List<Map<String, Object>> dataList = new ArrayList<>(pageSize);
            FindIterable<Document> documents = mongoClient.getDatabase(database).getCollection(collection)
                    .find(document).skip((page - 1) * pageSize).limit(pageSize);
            for (Document item : documents) {
                dataList.add(document2Map(item));
            }
            return dataList;
        } else {
            if (CollectionOperateTypeEnum.ADD.code.equals(operateType)) {
                //添加数据
                mongoClient.getDatabase(database).getCollection(collection)
                        .insertOne((Document) document);
                return "success";
            }
            if (CollectionOperateTypeEnum.MODIFY.code.equals(operateType)) {
                //修改数据
                String id = paramJson.getJSONObject("currentRow").getString("id");
                if (StrUtil.isBlank(id)) {
                    throw new IllegalArgumentException("id参数不能为空");
                }
                mongoClient.getDatabase(database).getCollection(collection)
                        .updateOne(Filters.eq("id", id), document);
                return "success";
            }
            if (CollectionOperateTypeEnum.DELETE.code.equals(operateType)) {
                //删除数据
                String id = paramJson.getJSONObject("currentRow").getString("id");
                if (StrUtil.isBlank(id)) {
                    throw new IllegalArgumentException("id参数不能为空");
                }
                mongoClient.getDatabase(database).getCollection(collection)
                        .deleteOne(Filters.eq("id", id));
                return "success";
            }
            if (CollectionOperateTypeEnum.DETAIL.code.equals(operateType)) {
                //查询详情
                String id = paramJson.getJSONObject("currentRow").getString("id");
                if (StrUtil.isBlank(id)) {
                    throw new IllegalArgumentException("id参数不能为空");
                }
                FindIterable<Document> documents = mongoClient.getDatabase(database).getCollection(collection)
                        .find(Filters.eq("id", id));
                Map<String, Object> map = new HashMap<>();
                if (CollUtil.isNotEmpty(documents)) {
                    for (Document item : documents) {
                        map = document2Map(item);
                        break;
                    }
                }
                return map;
            }
            if (CollectionOperateTypeEnum.QUERY.code.equals(operateType)) {
                //查询数据
                List<Map<String, Object>> dataList = new ArrayList<>(pageSize);
                FindIterable<Document> documents = mongoClient.getDatabase(database).getCollection(collection)
                        .find(document).limit(1000);
                for (Document item : documents) {
                    dataList.add(document2Map(item));
                }
                return dataList;
            }
        }
        return "未知操作";
    }

}
